package lt.ntec.sudokusolver.engine;

import java.util.ArrayList;

import lt.ntec.sudokusolver.Result;
import lt.ntec.sudokusolver.Solvable;

public class Solver implements Solvable{

	private Board board;

	private static int LOOP_MAX = 10000;

	private boolean solved = false;
	private int bogusValues = 0;
	private int solvedValues = 0;

	public Result solve(int[][] nums) {
		board = new Board(nums);
		int maxLoop = LOOP_MAX;
		State currentState = new State(board.getNums(), 0, null, null);

		while (solved != true && maxLoop-- > 0) {

			bogusValues = 0;
			solvedValues = 0;
			int possibleValues = 9;
			Cell branchPoint = null;
			if (currentState.isSolvable()) {
				for (Cell cell : board.getCells()) {
					if (cell.getNumber() != 0) {
						continue;
					}
					ArrayList<Integer> posValues = cell.getPossibleValues();
					if (posValues.size() == 0) {
						currentState.setSolvable(false);
						break;
					}
					if (posValues.size() == 1) {
						solvedValues++;
						cell.setNumber(posValues.get(0));
					} else {
						bogusValues++;
					}
				}
			}


			if (currentState.isSolvable() == false) {

				board.setNums(currentState.getNums());
				int newNum = currentState.getChosen() + 1;
				currentState.setChosen(newNum);

				ArrayList<Integer> values = currentState.getCell()
						.getPossibleValues();

				if (values.size() <= newNum) {
					currentState.setSolvable(false);
					if (currentState.getPrevious() == null) {
						return new Result(board.getNums(),
								Result.Type.UNSOLVABLE);
					}
					currentState = currentState.getPrevious();
					currentState.setSolvable(false);
					board.setNums(currentState.getNums());
					continue;
				}

				currentState.getCell().setNumber(values.get(newNum));
				currentState.setSolvable(true);

			} else if (solvedValues == 0 && bogusValues > 0) {
				for (Cell cell : board.getCells()) {
					if (cell.getNumber() != 0) {
						continue;
					}

					ArrayList<Integer> posValues = cell.getPossibleValues();
					if (possibleValues > posValues.size()) {
						possibleValues = posValues.size();
						branchPoint = cell;
					}

					if (possibleValues <= 1) {
						throw new RuntimeException("This cannot be");
					}
				}

				currentState = new State(board.getNums(), 0, branchPoint,
						currentState);

				branchPoint.setNumber(branchPoint.getPossibleValues().get(0));
			} else if (bogusValues == 0 && currentState.isSolvable() == true) {
				solved = true;
			}

		}
		if (maxLoop <= 0) {
			throw new RuntimeException("Too long to count");
		}

		return new Result(board.getNums(), Result.Type.SOLVED);
	}
}
